/*******************************************************************************
 *  Floppy Dsik Controller for BBK/YuXing
 *
 *  Author:  fanoble <87430545@qq.com>
 *
 *  Create:   Nov 6, 2022, by fanoble
 *******************************************************************************
 */

#ifdef WIN32
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#else
#include "ff.h"
#include "nes_main.h"
#endif
#include <stdlib.h>
#include <string.h>

#include "nes_fdc.h"

// http://cpctech.cpc-live.com/docs/upd765a/necfdc.htm
// https://www.cpcwiki.eu/index.php/765_FDC

//static const FDC::FDC_CMD_DESC FdcCmdTable[32] =
static const FDC_CMD_DESC FdcCmdTable[32] =
{
	/* 0x00 */ { 1, 1, FdcNop },
	/* 0x01 */ { 1, 1, FdcNop },
	/* 0x02 */ { 9, 7, FdcReadTrack },
	/* 0x03 */ { 3, 0, FdcSpecify },
	/* 0x04 */ { 2, 1, FdcSenseDriveStatus },
	/* 0x05 */ { 9, 7, FdcWriteData },
	/* 0x06 */ { 9, 7, FdcReadData },
	/* 0x07 */ { 2, 0, FdcRecalibrate },
	/* 0x08 */ { 1, 2, FdcSenseIntStatus },
	/* 0x09 */ { 9, 7, FdcWriteDeletedData },
	/* 0x0A */ { 2, 7, FdcReadID },
	/* 0x0B */ { 1, 1, FdcNop },
	/* 0x0C */ { 9, 7, FdcReadDeletedData },
	/* 0x0D */ { 6, 7, FdcFormatTrack },
	/* 0x0E */ { 1, 1, FdcNop },
	/* 0x0F */ { 3, 0, FdcSeek },
	/* 0x10 */ { 1, 1, FdcNop },
	/* 0x11 */ { 9, 7, FdcScanEqual },
	/* 0x12 */ { 1, 1, FdcNop },
	/* 0x13 */ { 1, 1, FdcNop },
	/* 0x14 */ { 1, 1, FdcNop },
	/* 0x15 */ { 1, 1, FdcNop },
	/* 0x16 */ { 1, 1, FdcNop },
	/* 0x17 */ { 1, 1, FdcNop },
	/* 0x18 */ { 1, 1, FdcNop },
	/* 0x19 */ { 9, 7, FdcScanLowOrEqual },
	/* 0x1A */ { 1, 1, FdcNop },
	/* 0x1B */ { 1, 1, FdcNop },
	/* 0x1C */ { 1, 1, FdcNop },
	/* 0x1D */ { 9, 7, FdcScanHighOrEqual },
	/* 0x1E */ { 1, 1, FdcNop },
	/* 0x1F */ { 1, 1, FdcNop },
};

/*
 * SENSE INTERRUPT STATUS
 * An Interrupt signal is generated by the FDC for one of the following reasons:
 * 1. Upon entering the Result Phase of:
 *    a. Read Data Command
 *    b. Read a Track Command
 *    c, Read ID Command
 *    d. Read Deleted Data Command
 *    e. Write Data Command
 *    f. Format a Cylinder Command
 *    g. Write Deleted Data Command
 *    h. Scan Commands
 * 2. Ready Line of FDD changes state
 * 3. End of Seek or Recalibrate Command
 * 4. During Execution Phase in the NON-DMA Mode
 */

/*
 * Interrupts caused by reasons 1 and 4 above occur during normal command operations and are easily discernible
 * by the processor. During an execution phase in NON-OMA Mode, DB5 in Main Status Register
 * is [high]. Upon entering Result Phase this bit gets clear. Reason 1 and 4 does not require Sense Interrupt
 * Status command. The interrupt is cleared by reading/writing data to FDC. Interrupts caused by reasons
 * 2 and 3 above may be uniquely identified with the aid of the Sense Interrupt Status Command. This
 * command when issued resets the interrupt signal and via bits 5, 6, and 7 of Status Register 0 identifies
 * the cause of the interrupt.
 */

//FDC::FDC()
void FdcInit(FDC* thiz)
{
	thiz->bFdcIrq = 0;
	thiz->bFdcHwReset = 0;
	thiz->bFdcSoftReset = 0;

	thiz->bFdcDmaInt = 0;
	thiz->nFdcDrvSel = 0;
	thiz->nFdcMotor = 0;
	thiz->nFdcMainStatus = FDC_MS_RQM;

	thiz->nFDCStatus[0] = 0;
	thiz->nFDCStatus[1] = 0;
	thiz->nFDCStatus[2] = 0;
	thiz->nFDCStatus[3] = 0;

	thiz->bFdcCycle = 0;
	thiz->bFdcPhase = FDC_PH_IDLE;

	thiz->nFdcCylinder = 0;

	thiz->pFdcDataPtr = 0;

	thiz->bDirty = 0;
	thiz->nDiskSize = 0;
	thiz->pDiskImage = 0;

	thiz->nCurrentLBA = 0;
}

//FDC::~FDC()
void FdcDone(FDC* thiz)
{
#ifdef WIN32
	if (thiz->pDiskImage) free(thiz->pDiskImage);
#endif
}

//==============================================================================

//int FDC::LoadDiskImage(const char* fname)
int FdcLoadDiskImage(FDC* thiz, const char* fname)
{
#ifdef WIN32
	FILE* fp;

	// try to load disk image
	if (!(fp = fopen(fname, "rb"))) {
		return 0;
	}

	fseek(fp, 0, SEEK_END);
	thiz->nDiskSize = ftell(fp);
	fseek(fp, 0, SEEK_SET);

	if (thiz->pDiskImage) free(thiz->pDiskImage);

	thiz->nDiskSize &= ~512; // aligned by 512 bytes
	if (thiz->nDiskSize > 0x200000)
		thiz->nDiskSize = 0x200000;

	if (!(thiz->pDiskImage = (unsigned char*)malloc(thiz->nDiskSize))) {
		fclose(fp);
		return 0;
	}

	fread(thiz->pDiskImage, thiz->nDiskSize, 1, fp);
	fclose(fp);
#else
	FIL file;
	UINT br;
	u8 res = 0;

	res = f_open(&file, fname, FA_READ);
	if (res != FR_OK) {
		return 0;
	}

	thiz->nDiskSize = f_size(&file);
	thiz->nDiskSize &= ~512; // aligned by 512 bytes
	if (thiz->nDiskSize > 0x200000)
		thiz->nDiskSize = 0x200000;

	thiz->pDiskImage = (unsigned char*)FDC_IMAGE_ADDRESS;

	f_read(&file, thiz->pDiskImage, thiz->nDiskSize, &br);

	f_close(&file);
#endif

	thiz->bDirty = 0;
	strcpy(thiz->szDiskName, fname);

	return 1;
}

//int FDC::SaveDiskImage()
int FdcSaveDiskImage(FDC* thiz)
{
#ifdef WIN32
	FILE* fp;

	if (!thiz->bDirty)
		return 0;

	if (!(fp = fopen(thiz->szDiskName, "wb"))) {
		return 0;
	}
	
	if (!fwrite(thiz->pDiskImage, thiz->nDiskSize, 1, fp)) {
		fclose(fp);
		return 0;
	}

	fclose(fp);
#else
	FIL f;
	int res;

	res = f_open(&f, thiz->szDiskName, FA_CREATE_ALWAYS | FA_WRITE);
	if (FR_OK == res) {
		UINT bw;

		f_write(&f, thiz->pDiskImage, thiz->nDiskSize, &bw);
		f_close(&f);

		sysprintf("save disk image file: %s\r\n", thiz->szDiskName);
	}
#endif

	thiz->bDirty = 0;

	return 1;
}

int FdcIsDirty(FDC* thiz)
{
	return thiz->bDirty;
}

//==============================================================================

//unsigned char FDC::Read(unsigned char nPort)
unsigned char FdcRead(FDC* thiz, unsigned char nPort)
{
	unsigned char nData;

	switch (nPort) {
		case 0: // 3F0: FDCDMADackIO
		case 1: // 3F1: FDCDMATcIO
			nData = *thiz->pFdcDataPtr++;
			thiz->bFdcDataBytes--;
			if (0 == thiz->bFdcDataBytes)
				thiz->bFdcPhase = FDC_PH_RESULT;
			break;
		case 2: // 3F2: FDCDRQPortI/FDCCtrlPortO
			// I: D6 : FDC DRQ
			nData = 0x40;
			break;
		case 3: // 3F3: FDCIRQPortI/FDCDMADackIO
			// I: D6 : IRQ
			if (thiz->bFdcIrq)
				nData = 0x40;
			else
				nData = 0;
			break;

		case 4: // 3F4: FDCResetPortO/FDCStatPortI
			// I: D7 : FDC ready
			// I: D6 : FDC dir
			// I: D5 : FDC busy
			nData = thiz->nFdcMainStatus;
			break;
		case 5: // 3F5: FDCDataPortIO
			if (FDC_PH_EXECUTION == thiz->bFdcPhase) {
				// Non-DMA mode
				switch (thiz->bFdcLastCommand) {
					case 0x02: // ReadTrack
					case 0x06: // ReadData
						nData = *thiz->pFdcDataPtr++;

						thiz->bFdcDataBytes--;
						if (0 == thiz->bFdcDataBytes) {
							thiz->bFdcPhase = FDC_PH_RESULT;
						}
						break;
					default:
						// who call me?
						break;
				}
			} else if (FDC_PH_RESULT == thiz->bFdcPhase) {
				nData = thiz->bFdcResults[thiz->bFdcCycle];
				thiz->bFdcCycle++;
				if (thiz->bFdcCycle == thiz->pFdcCmd->bRLength) {
					// prepare for next command
					thiz->bFdcCycle = 0;
					thiz->bFdcPhase = FDC_PH_IDLE;
					thiz->nFdcMainStatus &= ~FDC_MS_DATA_IN; // host to fdc

					thiz->bFdcIrq = 0;
				}
			} else {
				nData = 0;
			}
			break;
		case 7: // 3F7: FDCChangePortI/FDCSpeedPortO
			// I: D7 : Disk changed
			nData = 0;
			break;
		default:
			nData = 0;
			break;
	}

	return nData;
}

//void FDC::Write(unsigned char nPort, unsigned nData)
void FdcWrite(FDC* thiz, unsigned char nPort, unsigned nData)
{
	switch (nPort) {
		case 0: // 3F0: FDCDMADackIO
		case 1: // 3F1: FDCDMATcIO
			*thiz->pFdcDataPtr++ = nData;
			thiz->bDirty = 1;
			thiz->bFdcDataBytes--;
			if (0 == thiz->bFdcDataBytes) {
				thiz->bFdcCycle = 0;
				thiz->bFdcPhase = FDC_PH_RESULT;
				thiz->nFdcMainStatus |= FDC_MS_DATA_IN; // fdc to host
			}
			break;
		case 2: // 3F2: FDCDRQPortI/FDCCtrlPortO
			// O: D5 : Drv B motor
			// O: D4 : Drv A motor
			// O: D3 : Enable INT and DMA
			// O: D2 : not FDC Reset
			// O: D[1:0] : Drv sel

			thiz->bFdcDmaInt = (nData & 8) ? 1 : 0;
			thiz->nFdcDrvSel = nData & 3;
			thiz->nFdcMotor = nData >> 4;

			if (nData & 4) {
				if (thiz->bFdcSoftReset) {
					FdcSoftReset(thiz);

					thiz->bFdcSoftReset = 0;

					// IRQ after soft reset
					if (0 == thiz->nFdcDrvSel) {
						// Driver A Only
						thiz->bFdcIrq = thiz->pDiskImage ? 1 : 0;
					} else {
						thiz->bFdcIrq = 0;
					}
				}
			} else {
				if (!thiz->bFdcSoftReset) {
					thiz->bFdcSoftReset = 1;
					thiz->bFdcIrq = 0;
				}
			}

			break;
		case 3: // 3F3: FDCIRQPortI/FDCDMADackIO
			// I: D6 : IRQ
			nData = nData;
			break;
		case 4: // 3F4: FDCResetPortO/FDCStatPortI
			// O: D6 : FDC pin reset
			if (nData & 0x40) {
				if (!thiz->bFdcHwReset) {
					thiz->bFdcHwReset = 1;
					thiz->bFdcIrq = 0;
				}
			} else {
				if (thiz->bFdcHwReset) {
					FdcHardReset(thiz);
					thiz->bFdcHwReset = 0;
				}
			}
			break;
		case 5: // 3F5: FDCDataPortIO
			switch (thiz->bFdcPhase) {
				case FDC_PH_EXECUTION:
					// Non-DMA mode
					switch (thiz->bFdcLastCommand) {
						case 0x05: // WriteData
							thiz->bDirty = 1;
							*thiz->pFdcDataPtr++ = nData;
							thiz->bFdcDataBytes--;
							if (0 == thiz->bFdcDataBytes) {
								thiz->bFdcCycle = 0;
								thiz->bFdcPhase = FDC_PH_RESULT;
								thiz->nFdcMainStatus |= FDC_MS_DATA_IN; // fdc to host

								thiz->bFdcIrq = 0;
							}
							break;
						case 0x0D: // FormatTrack
							thiz->bFdcDataBytes--;
							if (0 == thiz->bFdcDataBytes) {
								thiz->bFdcCycle = 0;
								thiz->bFdcPhase = FDC_PH_RESULT;
								thiz->nFdcMainStatus |= FDC_MS_DATA_IN; // fdc to host

								thiz->bFdcIrq = 0;
							}
							break;
						default:
							// ERROR
							break;
					}
					break;
				case FDC_PH_RESULT:
					// ERROR
					break;
				case FDC_PH_IDLE:
				default:
					thiz->bFdcCycle = 0;
					thiz->bFdcPhase = FDC_PH_COMMAND;
					thiz->pFdcCmd = &FdcCmdTable[nData & FDC_CC_MASK];
					// fall through
				case FDC_PH_COMMAND:
					thiz->bFdcCommands[thiz->bFdcCycle] = nData;
					thiz->bFdcCycle++;
					if (thiz->bFdcCycle == thiz->pFdcCmd->bWLength) {
						int cmd = thiz->bFdcCommands[0] & FDC_CC_MASK;

						thiz->pFdcCmd->pFun(thiz);

						thiz->bFdcCycle = 0;
						thiz->bFdcLastCommand = cmd;
					}
					break;
			}
			break;
		case 7: // 3F7: FDCChangePortI/FDCSpeedPortO
			// I: D7 : Disk changed
			// O: D[1:0] : 00 500kbps(1.2M, 1.44M)
			//             01 300kbps(360K)
			//             10 250kbps(720K)
			nData = nData;
			break;
		default:
			break;
	}
}

//==============================================================================

//void FDC::FdcHardReset(void)
void FdcHardReset(FDC* thiz)
{
	thiz->bFdcDmaInt = 0;
	thiz->nFdcDrvSel = 0;
	thiz->nFdcMotor = 0;
	thiz->nFdcMainStatus = FDC_MS_RQM;

	thiz->nFDCStatus[0] = 0;
	thiz->nFDCStatus[1] = 0;
	thiz->nFDCStatus[2] = 0;
	thiz->nFDCStatus[3] = 0;

	thiz->bFdcIrq = 0;
	thiz->bFdcCycle = 0;
	thiz->bFdcPhase = FDC_PH_IDLE;
}

//void FDC::FdcSoftReset(void)
void FdcSoftReset(FDC* thiz)
{
	thiz->nFdcDrvSel = 0;
	thiz->nFdcMotor = 0;
	thiz->nFdcMainStatus = FDC_MS_RQM;

	thiz->nFDCStatus[0] = 0;
	thiz->nFDCStatus[1] = 0;
	thiz->nFDCStatus[2] = 0;
	thiz->nFDCStatus[3] = 0;

	thiz->bFdcCycle = 0;
	thiz->bFdcPhase = FDC_PH_IDLE;
}

//void FDC::FdcNop(FDC* thiz)
void FdcNop(FDC* thiz)
{
	thiz->nFDCStatus[0]   = FDC_S0_IC1;
	thiz->bFdcResults[0]  = thiz->nFDCStatus[0];
	thiz->bFdcPhase       = FDC_PH_RESULT;
	thiz->nFdcMainStatus |= FDC_MS_DATA_IN; // fdc to host
}

//void FDC::FdcReadTrack(FDC* thiz)
void FdcReadTrack(FDC* thiz)
{
	thiz = thiz;

	thiz->bFdcPhase = (thiz->nFdcMainStatus & FDC_MS_EXECUTION) ?
					  FDC_PH_EXECUTION : FDC_PH_RESULT;

	thiz->nFdcMainStatus |= FDC_MS_DATA_IN; // fdc to host
}

//void FDC::FdcSpecify(FDC* thiz)
void FdcSpecify(FDC* thiz)
{
	// 03, DF, 03
	// [1] Step Rate Time, Head Unload Time
	// [2] HLT/ND
	int ND = thiz->bFdcCommands[2] & 1;

	if (ND)
		thiz->nFdcMainStatus |= FDC_MS_EXECUTION;
	else
		thiz->nFdcMainStatus &= ~FDC_MS_EXECUTION;

	thiz->bFdcPhase = FDC_PH_IDLE;
}

//void FDC::FdcSenseDriveStatus(FDC* thiz)
void FdcSenseDriveStatus(FDC* thiz)
{
	thiz->bFdcPhase = FDC_PH_RESULT;
	thiz->nFdcMainStatus |= FDC_MS_DATA_IN; // fdc to host
}

//void FDC::FdcWriteData(FDC* thiz)
void FdcWriteData(FDC* thiz)
{
	unsigned char C = thiz->bFdcCommands[2];
	unsigned char H = thiz->bFdcCommands[3];
	unsigned char R = thiz->bFdcCommands[4];
	unsigned char N = thiz->bFdcCommands[5];

	int LBA;

	LBA = C * 36 + H * 18 + (R - 1);

	thiz->nCurrentLBA = LBA;

	thiz->pFdcDataPtr = thiz->pDiskImage + LBA * 512;

	thiz->bFdcDataBytes = 512;

	R++;
	if (19 == R) {
		R = 1;
		H++;
		if (2 == H) {
			C++;
			if (80 == C)
				C = 0;
		}
	}

	thiz->nFDCStatus[0] = 0;

	thiz->bFdcResults[0] = (thiz->nFdcMainStatus & FDC_MS_EXECUTION) ? FDC_S0_IC0 : 0; // ST0
	thiz->bFdcResults[1] = FDC_S1_EN; // ST1
	thiz->bFdcResults[2] = 0; // ST2
	thiz->bFdcResults[3] = C;
	thiz->bFdcResults[4] = H;
	thiz->bFdcResults[5] = R;
	thiz->bFdcResults[6] = N;

	thiz->bFdcIrq = 1;

	// host to fdc
	thiz->nFdcMainStatus &= ~FDC_MS_DATA_IN;

	thiz->bFdcPhase = (thiz->nFdcMainStatus & FDC_MS_EXECUTION) ?
					  FDC_PH_EXECUTION : FDC_PH_RESULT;
}

//void FDC::FdcReadData(FDC* thiz)
void FdcReadData(FDC* thiz)
{
	unsigned char C = thiz->bFdcCommands[2];
	unsigned char H = thiz->bFdcCommands[3];
	unsigned char R = thiz->bFdcCommands[4];
	unsigned char N = thiz->bFdcCommands[5];

	int LBA;

	LBA = C * 36 + H * 18 + (R - 1);

	if (LBA * 512 >= thiz->nDiskSize) {
		// simplely point to the last sector
		LBA = thiz->nDiskSize / 512 - 1;
	}

	thiz->nCurrentLBA = LBA;

	thiz->pFdcDataPtr = thiz->pDiskImage + LBA * 512;

	thiz->bFdcDataBytes = 512;

	R++;
	if (19 == R) {
		R = 1;
		H++;
		if (2 == H) {
			C++;
			if (80 == C)
				C = 0;
		}
	}

	thiz->nFDCStatus[0] = 0;

	thiz->bFdcResults[0] = (thiz->nFdcMainStatus & FDC_MS_EXECUTION) ? FDC_S0_IC0 : 0; // ST0
	thiz->bFdcResults[1] = FDC_S1_EN; // ST1
	thiz->bFdcResults[2] = 0; // ST2
	thiz->bFdcResults[3] = C;
	thiz->bFdcResults[4] = H;
	thiz->bFdcResults[5] = R;
	thiz->bFdcResults[6] = N;

	thiz->bFdcIrq = 1;

	thiz->bFdcPhase = (thiz->nFdcMainStatus & FDC_MS_EXECUTION) ?
					  FDC_PH_EXECUTION : FDC_PH_RESULT;

	thiz->nFdcMainStatus |= FDC_MS_DATA_IN; // fdc to host
}

//void FDC::FdcRecalibrate(FDC* thiz)
void FdcRecalibrate(FDC* thiz)
{
	unsigned char US;

	US = thiz->bFdcCommands[1] & 3;

	thiz->nFDCStatus[0] = US ? (FDC_S0_SE | FDC_S0_IC0) : FDC_S0_SE;

	thiz->bFdcIrq = 1;
	thiz->bFdcPhase = FDC_PH_IDLE;
}

//void FDC::FdcSenseIntStatus(FDC* thiz)
void FdcSenseIntStatus(FDC* thiz)
{
	switch (thiz->bFdcLastCommand) {
		case 0x07: // FdcRecalibrate
			thiz->bFdcResults[0] = thiz->nFDCStatus[0];
			thiz->bFdcResults[1] = 0;	// PCN
			break;
		case 0x0F: // FdcSeek
			thiz->bFdcResults[0] = FDC_S0_SE;
			thiz->bFdcResults[1] = thiz->nFdcCylinder;	// PCN
			break;
		default:
			if (0 == thiz->nFdcDrvSel)	// Drv A Only
				thiz->nFDCStatus[0] = FDC_S0_IC0 | FDC_S0_IC1;
			else
				thiz->nFDCStatus[0] = FDC_S0_IC0 | FDC_S0_SE;

			thiz->bFdcResults[0] = thiz->nFDCStatus[0];
			thiz->bFdcResults[1] = thiz->nFdcCylinder;	// PCN
			break;
	}

	thiz->bFdcIrq = 0;
	thiz->bFdcPhase = FDC_PH_RESULT;
	thiz->nFdcMainStatus |= FDC_MS_DATA_IN; // fdc to host
}

//void FDC::FdcWriteDeletedData(FDC* thiz)
void FdcWriteDeletedData(FDC* thiz)
{
	thiz->bFdcPhase = (thiz->nFdcMainStatus & FDC_MS_EXECUTION) ?
					  FDC_PH_EXECUTION : FDC_PH_RESULT;
}

//void FDC::FdcReadID(FDC* thiz)
void FdcReadID(FDC* thiz)
{
	if (!thiz->pDiskImage) {
		// no response while disk empty
		thiz->bFdcPhase = FDC_PH_IDLE;
		return;
	}

	thiz->nFDCStatus[0] = 0;

	thiz->bFdcResults[0] = 0;
	thiz->bFdcResults[1] = thiz->pDiskImage ? 0 : (FDC_S1_ND | FDC_S1_MA); // ST1
	thiz->bFdcResults[2] = 0; // ST2
	thiz->bFdcResults[3] = 0;
	thiz->bFdcResults[4] = 0;
	thiz->bFdcResults[5] = 0;
	thiz->bFdcResults[6] = 0;

	thiz->bFdcIrq = 1;
	thiz->bFdcPhase = FDC_PH_RESULT;
	thiz->nFdcMainStatus |= FDC_MS_DATA_IN; // fdc to host
}

//void FDC::FdcReadDeletedData(FDC* thiz)
void FdcReadDeletedData(FDC* thiz)
{
	thiz->bFdcPhase = (thiz->nFdcMainStatus & FDC_MS_EXECUTION) ?
					  FDC_PH_EXECUTION : FDC_PH_RESULT;
}

//void FDC::FdcFormatTrack(FDC* thiz)
void FdcFormatTrack(FDC* thiz)
{
	unsigned char HD_US = thiz->bFdcCommands[1];
	unsigned char N = thiz->bFdcCommands[2]; // N: bytes per sector
	unsigned char SC = thiz->bFdcCommands[3]; // SC: sectors per track
	unsigned char GPL = thiz->bFdcCommands[4]; // GPL: gap 3 length
	unsigned char D = thiz->bFdcCommands[5]; // D: filler pattern to write in each byte

	thiz->pFdcDataPtr = thiz->pDiskImage + thiz->nCurrentLBA * 512;

	memset(thiz->pFdcDataPtr, D, 512);

	thiz->nFDCStatus[0] = 0;

	thiz->bFdcResults[0] = HD_US & 7;// FDC_S0_IC0; // ST0
	thiz->bFdcResults[1] = 0;// FDC_S1_EN; // ST1
	thiz->bFdcResults[2] = 0; // ST2
	thiz->bFdcResults[3] = 0;
	thiz->bFdcResults[4] = 0;
	thiz->bFdcResults[5] = 0;
	thiz->bFdcResults[6] = N; // bytes per sector

	thiz->bFdcIrq = 1;

	thiz->bFdcPhase = FDC_PH_RESULT;
	thiz->nFdcMainStatus |= FDC_MS_DATA_IN; // fdc to host
}

//void FDC::FdcSeek(FDC* thiz)
void FdcSeek(FDC* thiz)
{
	// new cylinder number
	unsigned char NCN;
	unsigned char US;
	unsigned char HD;

	HD = (thiz->bFdcCommands[1] >> 2) & 1;
	US = thiz->bFdcCommands[1] & 3;
	NCN = thiz->bFdcCommands[2];

	thiz->nFdcCylinder = NCN;

//	thiz->nCurrentLBA = NCN * 36 + HD * 18; for format_track command

	thiz->nFDCStatus[0] = FDC_S0_SE;

	thiz->bFdcIrq = 1;
	thiz->bFdcPhase = FDC_PH_IDLE;
}

//void FDC::FdcScanEqual(FDC* thiz)
void FdcScanEqual(FDC* thiz)
{
	thiz->bFdcPhase = (thiz->nFdcMainStatus & FDC_MS_EXECUTION) ?
					  FDC_PH_EXECUTION : FDC_PH_RESULT;
}

//void FDC::FdcScanLowOrEqual(FDC* thiz)
void FdcScanLowOrEqual(FDC* thiz)
{
	thiz->bFdcPhase = (thiz->nFdcMainStatus & FDC_MS_EXECUTION) ?
					  FDC_PH_EXECUTION : FDC_PH_RESULT;
}

//void FDC::FdcScanHighOrEqual(FDC* thiz)
void FdcScanHighOrEqual(FDC* thiz)
{
	thiz->bFdcPhase = (thiz->nFdcMainStatus & FDC_MS_EXECUTION) ?
					  FDC_PH_EXECUTION : FDC_PH_RESULT;
}

/*******************************************************************************
                           E N D  O F  F I L E
*******************************************************************************/
